package com.xj.util;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import java.io.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * .gz 文件压缩与解压缩
 * Created by Sunshaoguang on 2017/11/20.
 */
@Slf4j
public class ZipUtils {

    public static final int BUFFER = 1024;
    public static final String EXT = ".zip";

    /**
     * 压缩文件
     *
     * @param fileName 文件的路径的全名称
     * @throws FileNotFoundException
     * @throws IOException
     */
    public static void compress(String fileName) throws IOException {
        if (notExists(fileName)) {
            log.warn(">>>>>>>>> 压缩失败，文件:{}不存在", fileName);
            return;
        }
        File file = new File(fileName);
        FileInputStream fis = new FileInputStream(fileName);
        FileOutputStream fos = new FileOutputStream(fileName.substring(0, fileName.indexOf(".")) + EXT);
        ZipOutputStream zos = new ZipOutputStream(fos);
        zos.putNextEntry(new ZipEntry(file.getName()));
        compress(fis, zos);
    }

    /**
     * 批量压缩文件
     *
     * @param files   要压缩的文件全名称数组
     * @param zipFile 压缩后的压缩文件全名称
     * @return boolean 压缩结果
     * @author Chuanlei.Sun
     * @date 2019-07-23 11:54
     */
    public static boolean zip(String[] files, String zipFile) throws Exception {
        ZipOutputStream out = null;
        try {
            if (null != zipFile && files.length > 0) {
                for (String fileName : files) {
                    if (notExists(fileName)) {
                        log.warn(">>>>>>>>> 批量压缩文件失败，文件:{}不存在", fileName);
                        return false;
                    }
                }
            } else {
                log.warn(">>>>>>>>> 批量压缩文件失败，文件数组:{}为空或无元素", files);
                return false;
            }
            log.info(">>>>>> 批量压缩中...");
            out = new ZipOutputStream(new FileOutputStream(zipFile));
            BufferedOutputStream bo = new BufferedOutputStream(out);
            for (String filename : files) {
                File file = new File(filename);
                zip(out, file, file.getName(), bo);
            }
            log.info(">>>>>>>>> 压缩完成");
            return true;
        } catch (Exception e) {
            log.error(">>>>>>>>> 压缩文件异常:{}, 文件数组:{}, 压缩文件:{}", e, files, zipFile);
            return false;
        } finally {
            if (out != null) {
                out.flush();
                out.close();
            }
        }
    }

    /**
     * 压缩
     */
    private static void zip(ZipOutputStream out, File f, String base, BufferedOutputStream bo) throws Exception {
        if (f.isDirectory()) {
            File[] fl = f.listFiles();
            if (fl.length == 0) {
                out.putNextEntry(new ZipEntry(base + "/")); // 创建zip压缩进入点base
                log.info(base + "/");
            }
            for (int i = 0; i < fl.length; i++) {
                log.info("file:" + fl[i].getName());
                if (null != fl[i] && StringUtils.isNotEmpty(fl[i].getName()) && fl[i].getName().length() > 4 &&
                        ".XML".toUpperCase().equals(fl[i].getName().substring(fl[i].getName().length() - 4, fl[i].getName().length()).toUpperCase())) {
                    zip(out, fl[i], fl[i].getName(), bo); // 递归遍历子文件夹
                }
            }
        } else {
            out.putNextEntry(new ZipEntry(base)); // 创建zip压缩进入点base
            FileInputStream in = new FileInputStream(f);
            BufferedInputStream bi = new BufferedInputStream(in);
            byte b[] = new byte[1024];
            int off = 0;
            int len;
            while ((len = bi.read(b, off, 1024)) > -1) {
                out.write(b, 0, len); // 将字节流写入当前zip目录
            }
            bo.flush();
            bi.close();
            in.close(); // 输入流关闭
        }
    }

    private static boolean notExists(String fileName) {
        File file = new File(fileName);
        return !file.exists();
    }

    /**
     * 根据输入输出名称压缩文件
     *
     * @param inFile
     * @param outputFileName
     * @throws Exception
     */
    public static void compress(File inFile, String outputFileName)
            throws Exception {
        FileInputStream inputFile = new FileInputStream(inFile);
        FileOutputStream outputFile = new FileOutputStream(outputFileName);
        ZipOutputStream zos = new ZipOutputStream(outputFile);
        zos.putNextEntry(new ZipEntry(inFile.getName()));
        compress(inputFile, zos);
        inputFile.close();
        outputFile.flush();
        outputFile.close();
    }

    /**
     * 根据输入输出流压缩文件
     *
     * @param is
     * @param gos
     * @throws Exception
     */
    public static void compress(InputStream is, ZipOutputStream gos)
            throws IOException {

        int count;
        byte data[] = new byte[BUFFER];
        while ((count = is.read(data, 0, BUFFER)) != -1) {
            gos.write(data, 0, count);
        }
        is.close();
        // 关闭输入输出流
        gos.finish();
        gos.flush();
        gos.close();
    }

    /**
     * 根据文件全路径解压文件
     *
     * @param file 文件全路径
     * @throws IOException
     */
    public static void decompress(String file) throws IOException {
        FileInputStream fis = new FileInputStream(file);
        FileOutputStream fos = new FileOutputStream(file.replace(EXT, "") + ".txt");
        decompress(fis, fos);
        // 关闭输入输出流
        fis.close();
        fos.flush();
        fos.close();
    }

    /**
     * 解压文件
     *
     * @param inFileName  //需要解压的文件名称
     * @param outFileName //解压后的文件名称
     * @throws IOException
     */
    public static void decompress(String inFileName, String outFileName) throws IOException {
        FileInputStream inputFile = new FileInputStream(inFileName);
        FileOutputStream outputFile = new FileOutputStream(outFileName);
        decompress(inputFile, outputFile);
        inputFile.close();
        outputFile.flush();
        outputFile.close();
    }

    /**
     * 解压文件
     *
     * @param is
     * @param os
     * @throws IOException
     */
    public static void decompress(InputStream is, OutputStream os)
            throws IOException {

        ZipInputStream gis = new ZipInputStream(is);

        int count;
        byte data[] = new byte[BUFFER];
        while ((count = gis.read(data, 0, BUFFER)) != -1) {
            os.write(data, 0, count);
        }
        //关闭解压流
        gis.close();
    }

    //多个文件压缩成gzip文件
    public static void mutileFileToGzip(ArrayList<String> filePaths, String targetFileName) {
        try {
            File file = new File(targetFileName);
            FileOutputStream fout = new FileOutputStream(file);
            BufferedInputStream bin = null;
            ZipOutputStream zout = new ZipOutputStream(fout);
            for (String fileSource : filePaths) {
                File temp = new File(fileSource);
                zout.putNextEntry(new ZipEntry(temp.getName()));
                int c;
                bin = new BufferedInputStream(new FileInputStream(fileSource));
                while ((c = bin.read()) != -1) {
                    zout.write(c);
                }
                bin.close();
            }
            zout.close();
        } catch (Exception e) {
            log.error("ZipUtils.mutileFileToGzip  ERROR:", e);
        }
        log.warn(">>>>>>>>> 压缩成功！");
    }

    public static void unzip(String filePath) {
        File source = new File(filePath);
        if (source.exists()) {
            ZipInputStream zis = null;
            BufferedOutputStream bos = null;
            try {
                zis = new ZipInputStream(new FileInputStream(source));
                ZipEntry entry = null;
                while ((entry = zis.getNextEntry()) != null
                        && !entry.isDirectory()) {
                    File target = new File(source.getParent(), entry.getName());
                    if (!target.getParentFile().exists()) {
                        // 创建文件父目录
                        target.getParentFile().mkdirs();
                    }
                    // 写入文件
                    bos = new BufferedOutputStream(new FileOutputStream(target));
                    int read = 0;
                    byte[] buffer = new byte[1024 * 10];
                    while ((read = zis.read(buffer, 0, buffer.length)) != -1) {
                        bos.write(buffer, 0, read);
                    }
                    bos.flush();
                }
                zis.closeEntry();
            } catch (IOException e) {
                throw new RuntimeException(e);
            } finally {
                try {
               if(zis!=null)     zis.close();
               if(bos!=null)  bos.close();
               } catch (IOException e) {
                  log.error(e.getMessage());
               }

            }
        }
    }

    /**
     * 解压到指定目录
     *
     * @param zipPath 压缩文件名(含路径)
     * @param descDir 解压到目录
     */
    public static void unZipFiles(String zipPath, String descDir) throws IOException {
        File zipFile = new File(zipPath);
        String fileName = zipFile.getName();
        if (org.apache.commons.lang.StringUtils.isNotEmpty(fileName)) {
            String[] fileNameArr = fileName.split("\\.zip");
            descDir += File.separator + fileNameArr[0] ;
        }
        unZipFiles(zipFile, descDir);
    }

    /**
     * 解压文件到指定目录
     *
     * @param zipFile 压缩文件
     * @param descDir 解压到目录
     */
    public static void unZipFiles(File zipFile, String descDir) throws IOException {
        File pathFile = new File(descDir);
        if (!pathFile.exists()) {
            pathFile.mkdirs();
        }
        ZipFile zip = new ZipFile(zipFile, Charset.forName("gbk"));
        for (Enumeration entries = zip.entries(); entries.hasMoreElements(); ) {
            ZipEntry entry = (ZipEntry) entries.nextElement();
            String zipEntryName = entry.getName();
            InputStream in = zip.getInputStream(entry);
            String outPath = (descDir + "/" + zipEntryName).replaceAll("\\*", "/");
            //判断路径是否存在,不存在则创建文件路径
            File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
            if (!file.exists()) {
                file.mkdirs();
            }
            //判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
            if (new File(outPath).isDirectory()) {
                continue;
            }
            //输出文件路径信息
            log.info(outPath);
            OutputStream out = new FileOutputStream(outPath);
            byte[] buf1 = new byte[1024];
            int len;
            while ((len = in.read(buf1)) > 0) {
                out.write(buf1, 0, len);
            }
            in.close();
            out.close();
        }
        zip.close();
        log.info("******************解压完毕********************");
    }

}
